#include <mcp/client/simple_client.h>
#include <mcp/client/client.h>
#include <mcp/transport/transport_factory.h>
#include <mcp/error.h>
#include <mcp/types.h>

#include <iostream>
#include <sstream>
#include <chrono>
#include <thread>
#include <memory>

namespace mcp {
namespace client {

// Simple client implementation
struct SimpleClient::Impl {
    std::unique_ptr<Client> client;
    std::string transport_url;
    ClientConfig config;
    std::function<void(const std::string&, const json::Value&)> notification_handler;
    std::function<void(const std::string&)> error_handler;

    Impl(const std::string& url, const ClientConfig& cfg)
        : transport_url(url), config(cfg) {}

    bool initialize_client();
};

SimpleClient::SimpleClient(const std::string& transport_url, const ClientConfig& config)
    : impl_(std::make_unique<Impl>(transport_url, config)) {}

SimpleClient::~SimpleClient() {
    disconnect();
}

bool SimpleClient::Impl::initialize_client() {
    try {
        // Create transport based on URL
        auto transport_config = create_transport_config_from_url(transport_url);
        auto transport = TransportFactory::instance().create_transport(transport_config);

        // Create client
        client = std::make_unique<Client>(std::move(transport), config);

        // Set up handlers
        client->set_notification_handler([this](const std::string& method, const json::Value& params) {
            if (notification_handler) {
                notification_handler(method, params);
            }
        });

        client->set_error_handler([this](const Error& error) {
            if (error_handler) {
                error_handler(error.message());
            }
        });

        return true;
    } catch (const std::exception& e) {
        if (error_handler) {
            error_handler(std::string("Failed to initialize client: ") + e.what());
        }
        return false;
    }
}

bool SimpleClient::connect() {
    if (!impl_->client && !impl_->initialize_client()) {
        return false;
    }

    return impl_->client->connect();
}

void SimpleClient::disconnect() {
    if (impl_->client) {
        impl_->client->disconnect();
    }
}

bool SimpleClient::is_connected() const {
    return impl_->client && impl_->client->is_connected();
}

std::expected<json::Value, std::string> SimpleClient::get_server_info() {
    if (!impl_->client) {
        return std::unexpected("Client not initialized");
    }

    auto result = impl_->client->get_server_info();
    if (!result) {
        return std::unexpected(result.error().message());
    }
    return result.value();
}

std::expected<std::vector<types::Tool>, std::string> SimpleClient::list_tools() {
    if (!impl_->client) {
        return std::unexpected("Client not initialized");
    }

    auto result = impl_->client->list_tools();
    if (!result) {
        return std::unexpected(result.error().message());
    }
    return result.value();
}

std::expected<json::Value, std::string> SimpleClient::call_tool(const std::string& name,
                                                                  const json::Value& arguments) {
    if (!impl_->client) {
        return std::unexpected("Client not initialized");
    }

    auto result = impl_->client->call_tool(name, arguments);
    if (!result) {
        return std::unexpected(result.error().message());
    }
    return result.value();
}

std::expected<std::vector<types::Resource>, std::string> SimpleClient::list_resources() {
    if (!impl_->client) {
        return std::unexpected("Client not initialized");
    }

    auto result = impl_->client->list_resources();
    if (!result) {
        return std::unexpected(result.error().message());
    }
    return result.value();
}

std::expected<types::ResourceContent, std::string> SimpleClient::read_resource(const std::string& uri) {
    if (!impl_->client) {
        return std::unexpected("Client not initialized");
    }

    auto result = impl_->client->read_resource(uri);
    if (!result) {
        return std::unexpected(result.error().message());
    }
    return result.value();
}

std::expected<std::vector<types::Prompt>, std::string> SimpleClient::list_prompts() {
    if (!impl_->client) {
        return std::unexpected("Client not initialized");
    }

    auto result = impl_->client->list_prompts();
    if (!result) {
        return std::unexpected(result.error().message());
    }
    return result.value();
}

std::expected<std::vector<types::PromptMessage>, std::string> SimpleClient::call_prompt(
    const std::string& name,
    const json::Value& arguments) {
    if (!impl_->client) {
        return std::unexpected("Client not initialized");
    }

    auto result = impl_->client->call_prompt(name, arguments);
    if (!result) {
        return std::unexpected(result.error().message());
    }
    return result.value();
}

bool SimpleClient::send_log(types::LogLevel level, const std::string& message) {
    if (!impl_->client) {
        return false;
    }
    return impl_->client->send_log(level, message);
}

void SimpleClient::set_notification_handler(std::function<void(const std::string&, const json::Value&)> handler) {
    impl_->notification_handler = std::move(handler);
    if (impl_->client) {
        impl_->client->set_notification_handler(impl_->notification_handler);
    }
}

void SimpleClient::set_error_handler(std::function<void(const std::string&)> handler) {
    impl_->error_handler = std::move(handler);
    if (impl_->client) {
        impl_->client->set_error_handler([this](const Error& error) {
            impl_->error_handler(error.message());
        });
    }
}

// Utility functions for common operations
std::expected<std::string, std::string> call_tool_and_extract_text(
    SimpleClient& client,
    const std::string& tool_name,
    const json::Value& arguments) {
    auto result = client.call_tool(tool_name, arguments);
    if (!result) {
        return std::unexpected(result.error());
    }

    auto& result_value = result.value();
    if (result_value.contains("content") && result_value["content"].is_array()) {
        auto& content_array = result_value["content"];
        if (!content_array.empty()) {
            auto& first_item = content_array[0];
            if (first_item.contains("text")) {
                return first_item["text"].as_string();
            }
        }
    }

    return std::unexpected("Tool result does not contain text content");
}

std::expected<std::vector<types::Resource>, std::string> find_resources_by_uri_pattern(
    SimpleClient& client,
    const std::string& uri_pattern) {
    auto all_resources = client.list_resources();
    if (!all_resources) {
        return std::unexpected(all_resources.error());
    }

    std::vector<types::Resource> matching_resources;
    for (const auto& resource : all_resources.value()) {
        if (resource.uri.find(uri_pattern) != std::string::npos) {
            matching_resources.push_back(resource);
        }
    }

    return matching_resources;
}

std::expected<std::vector<types::Tool>, std::string> find_tools_by_name_pattern(
    SimpleClient& client,
    const std::string& name_pattern) {
    auto all_tools = client.list_tools();
    if (!all_tools) {
        return std::unexpected(all_tools.error());
    }

    std::vector<types::Tool> matching_tools;
    for (const auto& tool : all_tools.value()) {
        if (tool.name.find(name_pattern) != std::string::npos) {
            matching_tools.push_back(tool);
        }
    }

    return matching_tools;
}

// Convenience function to create simple client
std::unique_ptr<SimpleClient> create_simple_client(const std::string& transport_url,
                                                   const ClientConfig& config) {
    return std::make_unique<SimpleClient>(transport_url, config);
}

// Example usage utilities
void print_server_info(SimpleClient& client) {
    auto info = client.get_server_info();
    if (info) {
        std::cout << "Server Info:\n";
        auto& info_obj = info.value();
        if (info_obj.contains("name")) {
            std::cout << "  Name: " << info_obj["name"].as_string() << "\n";
        }
        if (info_obj.contains("version")) {
            std::cout << "  Version: " << info_obj["version"].as_string() << "\n";
        }
    } else {
        std::cout << "Failed to get server info: " << info.error() << "\n";
    }
}

void print_available_tools(SimpleClient& client) {
    auto tools = client.list_tools();
    if (tools) {
        std::cout << "Available Tools:\n";
        for (const auto& tool : tools.value()) {
            std::cout << "  - " << tool.name;
            if (!tool.description.empty()) {
                std::cout << ": " << tool.description;
            }
            std::cout << "\n";
        }
    } else {
        std::cout << "Failed to list tools: " << tools.error() << "\n";
    }
}

void print_available_resources(SimpleClient& client) {
    auto resources = client.list_resources();
    if (resources) {
        std::cout << "Available Resources:\n";
        for (const auto& resource : resources.value()) {
            std::cout << "  - " << resource.uri;
            if (!resource.name.empty()) {
                std::cout << " (" << resource.name << ")";
            }
            std::cout << "\n";
        }
    } else {
        std::cout << "Failed to list resources: " << resources.error() << "\n";
    }
}

void print_available_prompts(SimpleClient& client) {
    auto prompts = client.list_prompts();
    if (prompts) {
        std::cout << "Available Prompts:\n";
        for (const auto& prompt : prompts.value()) {
            std::cout << "  - " << prompt.name;
            if (!prompt.description.empty()) {
                std::cout << ": " << prompt.description;
            }
            std::cout << "\n";
        }
    } else {
        std::cout << "Failed to list prompts: " << prompts.error() << "\n";
    }
}

} // namespace client
} // namespace mcp